using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.ComponentModel;
using WinInterop = System.Windows.Interop;
using RSCore.Helper.WPF;

#region Copyright (c) 2006, 2007, 2008, 2009 Render Studio Foundation. All rights reserved.

/*
 Copyright (c) 2006, 2007, 2008, 2009 Render Studio Foundation. All rights reserved.
 This work is copyrighted in its entirety. Permission to use "Render Studio"
 material may be granted only after we receive a written request from you,
 and you receive written permission from us.

 While Render Studio Foundation provides the information herein to anyone,
 we retain copyright on all code, text, graphic images, and other content.
 This means that you may not distribute the code, the text or graphics to others without the
 express written permission of Render Studio Foundation; "mirror" this information on your server
 without our permission; or modify or reuse the code, text or graphics on our servers.
 You may print copies of the information for your own personal use; and, you may reference
 the Service from your own documents. Any commercial use of this material is prohibited without
 the written permission of Render Studio Foundation. In all copies of this information, you must
 retain this notice and any other copyright notices originally included with such information.

 This material may be protected by U.S. and International Copyright laws, and you are legally
 bound by the copyright notices on those systems.

 "Render Studio" logos and other official symbols or graphics may not be used in connection with any
 product or service that has not been expressly authorized in advance in writing or in any manner
 that is likely to cause confusion among the users of this or other Web sites or among the public.
 All other seals, graphics or trademarks of organizations not affiliated with Render Studio Foundation
 that appear on this site are the property of their respective owners.

 Render Studio Foundation reserve all other rights. 
*/

#endregion

#region Render Studio Source Code Licence

/*
This license governs use of the Render Studio software. If you use the software, you accept this license. If you do not accept the license, do not use the software.

1. Summary

The source code is made available to view for reference purposes or to contribute to the project only. Developers may not distribute or modify the code for commercial or non-commercial purposes without express Agreement of Render Studio Foundation.

2. Definitions

The terms "reproduce," "reproduction," and "distribution" have the same meaning here as under U.S. copyright law.
"Licensor" means the foundation "Render Studio Foundation".
"You" means the licensee of the software, who is not engaged in designing, developing, or testing other software, that has the same or substantially the same features or functionality as the software.
"Your company" means the company you worked for when you downloaded the software.
"Reference use" means use of the software within your company as a reference, in read only form, for the sole purposes of debugging and maintaining your products. For clarity, "reference use" does NOT include (a) the right to use the software for purposes of designing, developing, or testing other software, that has the same or substantially the same features or functionality as the software, and (b) the right to distribute the software outside of your company.
"Licensed patents" means any Licensor patent claims which read directly on the software as distributed by the Licensor under this license.

3. Grant of Rights

(A) Copyright Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive, worldwide, royalty-free copyright license to reproduce the software for reference use.
(B) Patent Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive, worldwide, royalty-free patent license under licensed patents for reference use.
4. Limitations
(A) No Trademark License- This license does not grant you any rights to use the Licensor's name, logo, or trademarks.
(B) If you begin patent litigation against the Licensor over patents that you think may apply to the software (including a cross-claim or counterclaim in a lawsuit), your license to the software ends automatically.
(C) The software is licensed "as-is." You bear the risk of using it. The Licensor gives no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the Licensor excludes the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
 */

#endregion

namespace RS.Helper.WPF
{

    public partial class WindowDecorator : System.Windows.Controls.ContentControl
    {

        #region Variables

        static public WindowDecorator MainWindow = null;

        private Window _parentWindow;
        private ContentPresenter _contentPresenter;
        private UpdatePanel _updatePanel;

        //---- Events
        public event EventHandler SettingsButtonClik;

        #endregion

        #region Constructor

        static WindowDecorator()
        {
            // Used to define the default style of this control.
            //DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowDecorator), new FrameworkPropertyMetadata(typeof(WindowDecorator)));
        }

        public WindowDecorator()
        {
            MainWindow = this;
            this.Loaded += new RoutedEventHandler(WindowDecorator_Loaded);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (DesignerProperties.GetIsInDesignMode(this))
                return;

            //---- Search for the parentWindow window
            if (!_setupForCorrectMaximizingInitialized)
            {
                FrameworkElement parent = (FrameworkElement)this.Parent;
                while (!(parent is Window) && parent != null)
                    parent = (FrameworkElement)parent.Parent;

                _parentWindow = (Window)parent;
            }

            if (!DesignerProperties.GetIsInDesignMode(this))
                SetupForCorrectMaximizing();

            _updatePanel = (UpdatePanel)SearchFrameworkElement("updatePanel");

            //---- Buttons events
            ((TextBlock)SearchFrameworkElement("txtRenderStudio")).MouseDown += new MouseButtonEventHandler(txtRenderStudio_MouseDown);
            ((Button)SearchFrameworkElement("minimize")).Click += new RoutedEventHandler(this.minimize_Click);
            ((Button)SearchFrameworkElement("maximize")).Click += new RoutedEventHandler(this.maximize_Click);
            ((Button)SearchFrameworkElement("close")).Click += new RoutedEventHandler(this.close_Click);

            ((Border)SearchFrameworkElement("headerBar")).MouseLeftButtonDown += new MouseButtonEventHandler(headerBar_MouseLeftButtonDown);

            ((Line)SearchFrameworkElement("lnSizeNorth")).MouseDown += new MouseButtonEventHandler(OnSizeNorth);
            ((Line)SearchFrameworkElement("lnSizeSouth")).MouseDown += new MouseButtonEventHandler(OnSizeSouth);
            ((Line)SearchFrameworkElement("lnSizeWest")).MouseDown += new MouseButtonEventHandler(OnSizeWest);
            ((Line)SearchFrameworkElement("lnSizeEast")).MouseDown += new MouseButtonEventHandler(OnSizeEast);

            ((Rectangle)SearchFrameworkElement("rectSizeNorthWest")).MouseDown += new MouseButtonEventHandler(OnSizeNorthWest);
            ((Rectangle)SearchFrameworkElement("rectSizeNorthEast")).MouseDown += new MouseButtonEventHandler(OnSizeNorthEast);
            ((Rectangle)SearchFrameworkElement("rectSizeSouthWest")).MouseDown += new MouseButtonEventHandler(OnSizeSouthWest);
            ((Rectangle)SearchFrameworkElement("rectSizeSouthEast")).MouseDown += new MouseButtonEventHandler(OnSizeSouthEast);
        }

        object SearchFrameworkElement(string name)
        {
            object obj = Template.FindName(name, this);
            if (obj != null)
                return obj;
            return FindName(name);
        }

        void WindowDecorator_Loaded(object sender, RoutedEventArgs e)
        {
            //---- Setup the parentWindow window
            /*
            _parentWindow.AllowsTransparency = true;
            _parentWindow.WindowStyle = WindowStyle.None;
            _parentWindow.Background= Brushes.Transparent;
            _parentWindow.ResizeMode = ResizeMode.CanResizeWithGrip;
             */

            //---- Setup the events
            if (!DesignerProperties.GetIsInDesignMode(this))
            {
                _parentWindow.SizeChanged += new SizeChangedEventHandler(_parentWindow_SizeChanged);
                _parentWindow_SizeChanged(null, null);

                _parentWindow.StateChanged += new EventHandler(_parentWindow_StateChanged);
            }
        }

        #endregion

        #region SetBackgroundXAML

        public void SetBackgroundXAML(UIElement background)
        {
            Grid griBackgroundHolder = ((Grid)SearchFrameworkElement("griBackgroundHolder"));
            griBackgroundHolder.Children.Clear();
            griBackgroundHolder.Children.Add(background);
        }

        #endregion

        #region Events : Min/Max/Close selection buttons

        private void close_Click(object sender, RoutedEventArgs e)
        {
            _updatePanel.Close();
            _parentWindow.Close();
        }

        private void maximize_Click(object sender, RoutedEventArgs e)
        {
            if (((Path)Template.FindName("maximizeBox2", this)).Visibility == Visibility.Hidden)
                ((Path)Template.FindName("maximizeBox2", this)).Visibility = Visibility.Visible;
            else
                ((Path)Template.FindName("maximizeBox2", this)).Visibility = Visibility.Hidden;

            ((Canvas)Template.FindName("maximizeCanvas", this)).Width = 12;
            ((Canvas)Template.FindName("maximizeCanvas", this)).Height = 10;

            if (_parentWindow.WindowState == WindowState.Maximized)
                _parentWindow.WindowState = WindowState.Normal;
            else
                _parentWindow.WindowState = WindowState.Maximized;
        }

        private void minimize_Click(object sender, RoutedEventArgs e)
        {
            _parentWindow.WindowState = WindowState.Minimized;
        }

        //private void btnSettings_Click(object sender, RoutedEventArgs e)
        //{
        //    if (SettingsButtonClik != null)
        //        SettingsButtonClik(this, null);
        //}

        void txtRenderStudio_MouseDown(object sender, MouseButtonEventArgs e)
        {
            RSCore.Helper.Process.ProcessHelper.OpenUrl("http://www.renderstudio.org");
        }

        #endregion

        #region Maximizing management

        /*
		 * One interesting problem that comes to mind is the Window occupying all screen space on Maximizing
		 * when the WindowStyle is set to None.
		 * 
		 * So this means that even if we do have the taskbar visible the window would appear on "top" of that.
		 * This would not happen in the case of the normal window with the TitleBar.
		 * 
		 * To get this scenario working, we would have to include a small piece of code (by Josh Zana) in the Window.
		 */

        private bool _setupForCorrectMaximizingInitialized = false;

        private void SetupForCorrectMaximizing()
        {
            if (_setupForCorrectMaximizingInitialized)
                return;
            _setupForCorrectMaximizingInitialized = true;

            System.IntPtr handle = (new WinInterop.WindowInteropHelper(_parentWindow)).Handle;
            WinInterop.HwndSource.FromHwnd(handle).AddHook(new WinInterop.HwndSourceHook(WindowProc));
        }

        private static System.IntPtr WindowProc(
                                              System.IntPtr hwnd,
                                              int msg,
                                              System.IntPtr wParam,
                                              System.IntPtr lParam,
                                              ref bool handled)
        {
            switch (msg)
            {
                case 0x0024:/* WM_GETMINMAXINFO */
                    WmGetMinMaxInfo(hwnd, lParam);
                    handled = true;
                    break;
            }

            return (System.IntPtr)0;
        }

        private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
        {
            MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

            // Adjust the maximized size and position to fit the work area of the correct monitor
            int MONITOR_DEFAULTTONEAREST = 0x00000002;
            System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

            if (monitor != System.IntPtr.Zero)
            {
                MONITORINFO monitorInfo = new MONITORINFO();
                GetMonitorInfo(monitor, monitorInfo);
                RECT rcWorkArea = monitorInfo.rcWork;
                RECT rcMonitorArea = monitorInfo.rcMonitor;
                mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
                mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
                mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
                mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
            }

            Marshal.StructureToPtr(mmi, lParam, true);
        }

        #endregion

        #region Events : header bar

        private void headerBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.GetPosition(this).Y > 25)
                return;

            if (e.ClickCount > 1)
                this.maximize_Click(sender, e);
            else
                _parentWindow.DragMove();
        }

        #endregion

        #region Events : Parent window size changed

        void _parentWindow_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            WindowStateChanged();
        }

        void _parentWindow_StateChanged(object sender, EventArgs e)
        {
            WindowStateChanged();
        }

        private void WindowStateChanged()
        {
            if (_parentWindow.WindowState == WindowState.Minimized)
                return;

            if (_parentWindow.WindowState == WindowState.Maximized)
            {
                ((Line)SearchFrameworkElement("lnSizeNorth")).Visibility = Visibility.Collapsed;
                ((Line)SearchFrameworkElement("lnSizeSouth")).Visibility = Visibility.Collapsed;
                ((Line)SearchFrameworkElement("lnSizeWest")).Visibility = Visibility.Collapsed;
                ((Line)SearchFrameworkElement("lnSizeEast")).Visibility = Visibility.Collapsed;
                ((Rectangle)SearchFrameworkElement("rectSizeNorthWest")).Visibility = Visibility.Collapsed;
                ((Rectangle)SearchFrameworkElement("rectSizeNorthEast")).Visibility = Visibility.Collapsed;
                ((Rectangle)SearchFrameworkElement("rectSizeSouthWest")).Visibility = Visibility.Collapsed;
                ((Rectangle)SearchFrameworkElement("rectSizeSouthEast")).Visibility = Visibility.Collapsed;
            }
            else
            {
                ((Line)SearchFrameworkElement("lnSizeNorth")).Visibility = Visibility.Visible;
                ((Line)SearchFrameworkElement("lnSizeSouth")).Visibility = Visibility.Visible;
                ((Line)SearchFrameworkElement("lnSizeWest")).Visibility = Visibility.Visible;
                ((Line)SearchFrameworkElement("lnSizeEast")).Visibility = Visibility.Visible;
                ((Rectangle)SearchFrameworkElement("rectSizeNorthWest")).Visibility = Visibility.Visible;
                ((Rectangle)SearchFrameworkElement("rectSizeNorthEast")).Visibility = Visibility.Visible;
                ((Rectangle)SearchFrameworkElement("rectSizeSouthWest")).Visibility = Visibility.Visible;
                ((Rectangle)SearchFrameworkElement("rectSizeSouthEast")).Visibility = Visibility.Visible;
            }

            ((FrameworkElement)this.Parent).Width = _parentWindow.ActualWidth;
            ((FrameworkElement)this.Parent).Height = _parentWindow.ActualHeight;
            ((FrameworkElement)this.Parent).UpdateLayout();
        }

        #endregion

        #region Grip - Sizing event handlers

        void OnSizeSouth(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DragSize(SizingAction.South);
        }

        void OnSizeNorth(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DragSize(SizingAction.North);
        }

        void OnSizeEast(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DragSize(SizingAction.East);
        }

        void OnSizeWest(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DragSize(SizingAction.West);
        }

        void OnSizeNorthWest(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DragSize(SizingAction.NorthWest);
        }

        void OnSizeNorthEast(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DragSize(SizingAction.NorthEast);
        }

        void OnSizeSouthEast(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DragSize(SizingAction.SouthEast);
        }

        void OnSizeSouthWest(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            DragSize(SizingAction.SouthWest);
        }

        #endregion

        #region DragSize

        const int WM_SYSCOMMAND = 0x112;
        const int SC_SIZE = 0xF000;

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        private void DragSize(SizingAction sizingAction)
        {
            if (_parentWindow.WindowState != WindowState.Normal)
                return;

            System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(_parentWindow);
            if (System.Windows.Input.Mouse.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
            {
                //Helper.WPF.XamDataGridDragManagerGlobal.CanDrag = false;

                SendMessage(helper.Handle, WM_SYSCOMMAND, (IntPtr)(SC_SIZE + sizingAction), IntPtr.Zero);
                SendMessage(helper.Handle, 514, IntPtr.Zero, IntPtr.Zero);

                //Helper.WPF.XamDataGridDragManagerGlobal.CanDrag = true;
            }
        }

        #endregion

        #region Interop : Win32

        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            /// <summary>
            /// x coordinate of point.
            /// </summary>
            public int x;
            /// <summary>
            /// y coordinate of point.
            /// </summary>
            public int y;

            /// <summary>
            /// Construct a point of coordinates (x,y).
            /// </summary>
            public POINT(int x, int y)
            {
                this.x = x;
                this.y = y;
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct MINMAXINFO
        {
            public POINT ptReserved;
            public POINT ptMaxSize;
            public POINT ptMaxPosition;
            public POINT ptMinTrackSize;
            public POINT ptMaxTrackSize;
        };

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public class MONITORINFO
        {
            /// <summary>
            /// </summary>            
            public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));

            /// <summary>
            /// </summary>            
            public RECT rcMonitor = new RECT();

            /// <summary>
            /// </summary>            
            public RECT rcWork = new RECT();

            /// <summary>
            /// </summary>            
            public int dwFlags = 0;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 0)]
        public struct RECT
        {
            /// <summary> Win32 </summary>
            public int left;
            /// <summary> Win32 </summary>
            public int top;
            /// <summary> Win32 </summary>
            public int right;
            /// <summary> Win32 </summary>
            public int bottom;

            /// <summary> Win32 </summary>
            public static readonly RECT Empty = new RECT();

            /// <summary> Win32 </summary>
            public int Width
            {
                get { return Math.Abs(right - left); }  // Abs needed for BIDI OS
            }
            /// <summary> Win32 </summary>
            public int Height
            {
                get { return bottom - top; }
            }

            /// <summary> Win32 </summary>
            public RECT(int left, int top, int right, int bottom)
            {
                this.left = left;
                this.top = top;
                this.right = right;
                this.bottom = bottom;
            }


            /// <summary> Win32 </summary>
            public RECT(RECT rcSrc)
            {
                this.left = rcSrc.left;
                this.top = rcSrc.top;
                this.right = rcSrc.right;
                this.bottom = rcSrc.bottom;
            }

            /// <summary> Win32 </summary>
            public bool IsEmpty
            {
                get
                {
                    // BUGBUG : On Bidi OS (hebrew arabic) left > right
                    return left >= right || top >= bottom;
                }
            }
            /// <summary> Return a user friendly representation of this struct </summary>
            public override string ToString()
            {
                if (this == RECT.Empty) { return "RECT {Empty}"; }
                return "RECT { left : " + left + " / top : " + top + " / right : " + right + " / bottom : " + bottom + " }";
            }

            /// <summary> Determine if 2 RECT are equal (deep compare) </summary>
            public override bool Equals(object obj)
            {
                if (!(obj is Rect)) { return false; }
                return (this == (RECT)obj);
            }

            /// <summary>Return the HashCode for this struct (not garanteed to be unique)</summary>
            public override int GetHashCode()
            {
                return left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();
            }


            /// <summary> Determine if 2 RECT are equal (deep compare)</summary>
            public static bool operator ==(RECT rect1, RECT rect2)
            {
                return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom);
            }

            /// <summary> Determine if 2 RECT are different(deep compare)</summary>
            public static bool operator !=(RECT rect1, RECT rect2)
            {
                return !(rect1 == rect2);
            }


        }

        [DllImport("user32")]
        internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);

        /// <summary>
        /// 
        /// </summary>
        [DllImport("User32")]
        internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);

        #endregion

        #region Properties

        public WindowState WindowState
        {
            get
            {
                return _parentWindow.WindowState;
            }
            set
            {
                _parentWindow.WindowState = value;
            }
        }

        public bool IsMiniModeEnabled
        {
            set
            {
                ((Button)SearchFrameworkElement("miniMode")).IsEnabled = value;
            }
        }

        public UpdatePanel UpdatePanel
        {
            get
            {
                return _updatePanel;
            }
        }

        #endregion

    }
}